#include <stdio.h>
#include "agbmusic.h"
#include <stdlib.h>
#include <math.h>
#include "xm.h"

#include <stdarg.h>

#include "agb_sys.h"


/*
一般设置为0，
    可以防止一些其他的bug
    bug可能包括，一些乐器会循环到衰弱，直到没有
    .xm可能需要的是，把音量慢慢调节下来
*/
#define DUMP_LOOP 1


//输出debug信息
static FILE *_stderr = 0;
static FILE* _getstderr(){
    if(!_stderr){
        return stderr;
    }
    return _stderr;
}

#undef stderr
#define stderr _getstderr()


typedef struct{
    u8      old_event;
    u8      *mid;
    int     channel_num;
    double  clock;      //当前时钟
    int     note;       //按下
    int     note_vol;   //按下力度
    int     note_wait;  //等待多少tick才抬起
    int     note_down;  //按下
    int     wait;       //等待tick
    int     base_note;
    int     voice;      //当前音色
    int     old_voice;  //上一个音色
    int     vol;
    int     pan;
    int     bend;
    int     bendr;
    int     lfos;
    int     lfodl;
    int     mod;
    int     modt;
    int     tune;
    int     port;
    int     eot;
    int     tie;
}sound_track_t;

typedef struct{
    //模拟MusicPlayerTrack
    int     playing;    //正在播放
    int     has;        //声道存在
    int     note;       //agb note
    int     xm_note;    //当前xm_note键
    int     ch_num;
    int     att;
    int     dec;
    int     sus;
    int     rel;
    int     freq;   //当前样本的频率
    int     xmfreq; //xmfreq的频率
    sound_track_t *sound;   //跟随sound
    WaveData* wd;
    ToneDataArr *wd_tone;
    int     voice;
    int     old_voice;

    int     insn;   //当前乐器
    int     old_insn;   //上一个乐器

    int     vol;            //当前音量(att,sus,rel,dec)
    int     old_vol;        //保留上一个音量

    int     wait_clock;         //等待clock,应该是没有任何作用
    int     note_close_clock;   //关闭时的clock;
    //int     frame;              //当前帧数
    double  tick_frame;           //每节拍多少帧
    double  cur_frame;            //当前帧，如果超过1.0，则会计算vol

    file_t  *mid_file;
    int     read_pos;

    //
    int     bend_value;         //计算后的bend_value
    int     old_bend_value;     //上一个bend_value
}musicplayer_track_t;

file_t *insn_file;      //乐器文件
int     insn_num = 0;   //乐器数量
int     dump_index = 0; //导出音乐索引，如果为0，则全部导出
file_t *mid_file;
file_t *pat_file;

enum{
    music_track_num = 0x20,
};
static file_t *agb_track[0x10] = {0};
static sound_track_t tracks[0x20];
static musicplayer_track_t music_tracks[music_track_num];
static sound_track_t *cur_tracks;
ToneDataArr *cur_toneDataArr;   //当前音色表
static int tempo = 130/2;

static musicplayer_track_t *find_noplay_track(){
    int i;
    for(i=0;i<music_track_num;i++){
        if(!music_tracks[i].playing){
            return &music_tracks[i];
        }
    }
    return 0;
}

static int _toxm_note(int note,int base_note){
    note -= base_note;   //获得当前键相对基础键的偏移 base_note 是作为声音采样频率
    note += xm_Cn5;                 //xm默认使用C5作为声音采样频率
    return note;
}

static int toxm_note(int note){
    note -= cur_tracks->base_note;   //获得当前键相对基础键的偏移 base_note 是作为声音采样频率
    note += xm_Cn5;                 //xm默认使用C5作为声音采样频率
    return note;
}

static WaveData* get_sample(ToneDataArr*arr,int voice,int note,int *xm_note,int *freq,musicplayer_track_t*track){
    arr += voice;
    int t = arr->t.typ;
    if(t==TONEDATA_TYP_SPL){
        u8*splitbl = fix_rom(arr->s.st);
        int index = splitbl[note];
        arr = fix_rom(arr->s.ta);
        arr += index;
    }
    else if(t==TONEDATA_TYP_RHY){
        note += arr->ref.base_note;
        arr = fix_rom(arr->ref.data);
        arr += note;
    }

    if(arr->t.typ==0||arr->t.typ==8||arr->t.typ==1){
        track->wd_tone = arr;
        if(t==TONEDATA_TYP_RHY){
            //默认note
            note = cur_tracks->base_note;
            if(xm_note)*xm_note = xm_Cn5;
        }
        else if(t==TONEDATA_TYP_SPL){
            if(xm_note)*xm_note = _toxm_note(note,arr->t.key);
        }
        else{
            if(xm_note)*xm_note = _toxm_note(note,arr->t.key);
        }
        if(track){
            u8* rom_offset = (u8*)arr - rom + 0x08000000;
            track->att = arr->t.att;
            track->dec = arr->t.dec;
            track->sus = arr->t.sus;
            track->rel = arr->t.rel;
        }
        WaveData*wd = fix_rom(arr->t.wav);
        if(freq){
            *freq = MidiKey2Freq(wd,note,0);
        }
        return wd;
    }
    return 0;
}

static int _add_insn(ToneDataArr*arr,WaveData*wd){
    if(wd->stat&0xff){
        return wd->stat&0xff;
    }
    /*转换为采样频率*/
    int key = arr->t.key;
    //fprintf(stderr,"key:%d\n",key);
    int freq = agb2freq(wd->freq,key);
    u8 relnote,fine;
    /*转换为xm频率*/
    c5speed_relnote(freq,&relnote,&fine);
    int loop_start = 0;
    int loop_end = 0;
    int size = wd->size;
    #if DUMP_LOOP
    if(wd->stat&0x4000){
        //存在循环，需要加一个字节的大小
        loop_start = wd->loop;
        loop_end = wd->size;
        //size += 1;
    }
    #endif

    xm_gen_insn(insn_file,(u32)arr->t.wav,wd->data,size,freq,relnote,fine,loop_start,loop_end);
    insn_num++;
    wd->stat |= insn_num;

    return insn_num;
}

typedef enum{
    track_next = 0, //下一个row
    track_note = 1,
    track_vol =  2, //音量
    track_insn = 3, //更改乐器
    track_note_off, //note off
    track_portamento,   //xm的滑音
    track_portamento_down,
}track_event_t;

static void music_track_midi_gen(file_t*file,track_event_t e,int value){
    u16 v = e&0xf;
    v |= ((value&0xff)<<0x8);
    file_write(&v,sizeof(v),1,file);
}

static void music_track_set(musicplayer_track_t*music,sound_track_t*sound){
    //设置音频
    if(music){
        music->playing = 1;
        //获得按键
        int note = sound->note;
        int xm_note = 0;
        // ToneDataArr *arr = cur_toneDataArr;
        // arr += sound->voice;
        int freq;
        int xmfreq = 0;
        music->voice = sound->voice;
        if(sound->voice==24&&sound->note==0){
            fprintf(stderr,"%d\n",1);
        }
        music->note = note;
        music->wd = get_sample(cur_toneDataArr,sound->voice,note,&xm_note,&freq,music);

        //计算xm的频率
        music->xmfreq = c5_note_freq(MidiKey2fr(music->wd,60,0),xm_note);
        //fprintf(stderr,"freq:%d,%d\n",music->xmfreq,MidiKey2fr(music->wd,60,0));
        music->sound = sound;

        music->vol = music->att;    //设置当前音量
        music->insn = _add_insn(music->wd_tone,music->wd);
        music->freq = freq;
        music->xm_note = xm_note;
        music->note_close_clock = sound->note_wait;
        music->wait_clock = sound->wait;    //应该是没有任何作用
        //计算每个节拍多少帧
        music->tick_frame = row_per_second(tempo);
        music->tick_frame = 60 / music->tick_frame;
        music->cur_frame = 0.0;

        //设置该声道存在
        music->has = 1;
    }
}

//每帧计算一次
static void music_track_playing(musicplayer_track_t*music){
    if(music){
        if(music->playing){

            while(music->cur_frame>1.0){
                if(music->note_close_clock<=0){
                    //已经关闭note
                    music->vol *= music->rel;
                    music->vol >>= 8;
                    if(music->vol<=1){
                        //1表示没有音量了
                        music->vol = 0;
                    }
                }
                else{
                    //正在note
                    music->vol *= music->dec;
                    music->vol >>= 8;
                    if(music->vol<music->sus){
                        //小于持续值，则赋值持续值
                        music->vol = music->sus;
                    }
                }
                music->cur_frame -= 1.0;
            }

            /*
            4*(d[0xe]+d[0xf])
            */
            int bend = music->sound->bend * music->sound->bendr;
            bend *= 4;
            int note_off = bend>>8;
            music->bend_value = bend;
            bend &= 0xff;
            int freq = MidiKey2fr(music->wd,music->note + note_off,bend);
            fprintf(stderr,"freq:%d,%d,%d\n",freq,music->note + note_off,bend);

            music->cur_frame += music->tick_frame;  //执行一次节拍操作
            music->note_close_clock -= 1;

            if(music->xm_note){
                music_track_midi_gen(music->mid_file,track_note,music->xm_note);
                music->xm_note = 0;
            }
            else if(music->bend_value){
                //存在bend_value值
                //相差多少note和微调
                if(music->old_bend_value!=music->bend_value){
                    int bend_value = music->bend_value - music->old_bend_value;
                    int ext = track_portamento;
                    if(bend_value<0){
                        ext = track_portamento_down;
                        bend_value = -bend_value;
                    }
                    //
                    double vv = (double)(bend_value & 0xff) / 256.0;
                    vv += (double)(bend_value>>8);
                    #if 1
                    vv *= 64.0;
                    vv /= 4.0;   //进入DoFreqSlide，会*= 4
                    #elif 0
                    vv *= (double)xm_portamento_max / 12.0;  //64升半个音阶，而bend>>8 升一个note
                    #else
                    vv *= 16.0;     //启用linear frequency sides ，则一个单位为1/16.0
                    #endif
                    vv += 0.5f; //四舍五入
                    int v = vv;
                    if(v>255) v = 255;
                    music_track_midi_gen(music->mid_file,ext,v);
                }
                music->old_bend_value = music->bend_value;
            }

            if(music->vol==0){
                //关闭这个轨道
                music_track_midi_gen(music->mid_file,track_note_off,0);
                music->playing = 0;
                music->old_vol = 0;
            }
            else if(music->old_vol!=music->vol){
                //当音量发生改变时
                music->old_vol = music->vol;
                music_track_midi_gen(music->mid_file,track_vol,music->vol);
            }

            if(music->old_insn!=music->insn){
                //更改乐器
                music_track_midi_gen(music->mid_file,track_insn,music->insn);
                music->old_insn = music->insn;
            }
        }

        //下一个row
        music_track_midi_gen(music->mid_file,track_next,0);
    }

}

 static int _ply_fine(u8*mid);
 static int _ply_goto(u8*mid);
 static int _ply_patt(u8*mid);
 static int _ply_pend(u8*mid);
 static int _ply_rept(u8*mid);
 static int _ply_fine(u8*mid);
 static int _ply_fine(u8*mid);
 static int _ply_fine(u8*mid);
 static int _ply_fine(u8*mid);
 static int _ply_prio(u8*mid);
 static int _ply_tempo(u8*mid);
 static int _ply_keysh(u8*mid);
 static int _ply_voice(u8*mid);
 static int _ply_vol(u8*mid);
 static int _ply_pan(u8*mid);
 static int _ply_bend(u8*mid);
 static int _ply_bendr(u8*mid);
 static int _ply_lfos_rev01(u8*mid);
 static int _ply_lfodl(u8*mid);
 static int _ply_mod_rev01(u8*mid);
 static int _ply_modt(u8*mid);
 static int _ply_fine(u8*mid);
 static int _ply_fine(u8*mid);
 static int _ply_tune(u8*mid);
 static int _ply_fine(u8*mid);
 static int _ply_fine(u8*mid);
 static int _ply_fine(u8*mid);
 static int _ply_port(u8*mid);
 static int _ply_fine(u8*mid);
 static int _ply_endtie_rev01(u8*mid);
 static int _SampFreqSet_rev01(u8*mid);
 static int _TrackStop_rev01(u8*mid);
 static int _FadeOutBody_rev01(u8*mid);
 static int _TrkVolPitSet_rev01(u8*mid);
 static int _ClearChain(u8*mid);

 int _note(u8*mid){
     cur_tracks->note = mid[0];
     cur_tracks->note_down = 1;
     if(mid[1]<=127){
         cur_tracks->note_vol = mid[1];
         return 2;
     }
     return 1;
 }

 int _note_wait(u8*mid){
     //由于可能记录上一个事件，所以
     int nxx = cur_tracks->old_event;
     cur_tracks->note_wait = clock_tbl[nxx-N01+1];
     if(mid[0]<=127){
         return _note(mid)+0;
     }
     else{
         //上一个按键
         //没有改变note
     }
     cur_tracks->note_down = 1;
     return 0;
 }

 int _wait(u8*mid){
     cur_tracks->wait = clock_tbl[mid[0]-W00];
     return 1;
 }


static int _ply_fine(u8*mid);
 static int _ply_goto(u8*mid){
     //跳转到一个
     return 5;
 }
 static int _ply_patt(u8*mid){
     //会调用ply_goto
     return 5;
 }
 static int _ply_pend(u8*mid){
     //结束一个pat
     return 1;
 }
 static int _ply_rept(u8*mid){
     //重复一个
     return 5;
 }
 static int _ply_prio(u8*mid){
     //priority 优先级
     return 2;
 }
 static int _ply_tempo(u8*mid){
     tempo = mid[1];
     return 2;
 }

 static int _ply_keysh(u8*mid){
     cur_tracks->base_note = mid[1];
     return 2;
 }

 //下列会记录事件
 static int _ply_voice(u8*mid){
     cur_tracks->voice = mid[0];
     return 1;
 }
 static int _ply_vol(u8*mid){
     cur_tracks->vol = mid[0];
     return 1;
 }
 static int _ply_pan(u8*mid){
     cur_tracks->pan = mid[0];
     return 1;
 }
 static int _ply_bend(u8*mid){
     cur_tracks->bend = mid[0];
     cur_tracks->bend -= 0x40;
     return 1;
 }
 static int _ply_bendr(u8*mid){
     cur_tracks->bendr = mid[0];
     return 1;
 }
 static int _ply_lfos_rev01(u8*mid){
     cur_tracks->lfos = mid[0];
     return 1;
 }
 static int _ply_lfodl(u8*mid){
     cur_tracks->lfodl = mid[0];
     return 1;
 }
 static int _ply_mod_rev01(u8*mid){
     cur_tracks->mod = mid[0];
     return 1;
 }
 static int _ply_modt(u8*mid){
     cur_tracks->modt = mid[0];
     return 1;
 }
 static int _ply_fine(u8*mid){
     //结束
     cur_tracks->mid = 0;
     return 1;
 }
 static int _ply_tune(u8*mid){
     cur_tracks->tune = mid[0];
     return 1;
 }

 //0xcc
 static int _ply_port(u8*mid){
     cur_tracks->port = mid[0];
     return 1;
 }
 static int _ply_endtie_rev01(u8*mid){
     cur_tracks->eot = mid[0];
     return 1;
 }
 static int _SampFreqSet_rev01(u8*mid){
     cur_tracks->tie = mid[0];
     return 1;
 }
//  static int _TrackStop_rev01(u8*mid);
//  static int _FadeOutBody_rev01(u8*mid);
//  static int _TrkVolPitSet_rev01(u8*mid);
//  static int _ClearChain(u8*mid);

typedef int (*mid_event_fn_t)(u8*mid);
static mid_event_fn_t mid_event[0x100] = {
    [CnM2 ... Gn8] = _note,  //一般为N??,TIE,EOT的参数
    [W00 ... W96] = _wait,  //等待
    [N01 ... N96] = _note_wait,  //设置按键
    [KEYSH] = _ply_keysh,       //设置键的变化(基础,默认应该为60)
    [TEMPO] = _ply_tempo,       //设置速度
    [GOTO] = _ply_goto, //跳转表
    [PATT] = _ply_patt, //跳转表
    [PEND] = _ply_pend,
    [VOICE] = _ply_voice,   //音色
    [VOL] = _ply_vol,       //音量
    [PAN] = _ply_pan,       //
    [BEND] = _ply_bend,     //pitch bend (c_v+??)				音高
    [BENDR] = _ply_bendr,   //bend range						弯曲范围
    [LFOS] = _ply_lfos_rev01,     //LFO speed							低频振荡速度
    [LFODL] = _ply_lfodl,   //LFO delay							低频振荡衰减
    [MOD] = _ply_mod_rev01,       //modulation depth					调制深度
    [MODT] = _ply_modt,     //modulation type					调制类型
    [TUNE] = _ply_tune,     //micro tuning (c_v+??)				微调
    [EOT] = _ply_endtie_rev01,       //End of Tie
    [TIE] = _SampFreqSet_rev01,       //
    [_PORT] = _ply_port,
    [FINE] = _ply_fine,
};

static void song_proc(SongTable*song_table){
    SongHeader *so = fix_rom(song_table->so);
    file_seek(mid_file,0,SEEK_SET);
    file_seek(pat_file,0,SEEK_SET);
    int pat_num = 0;
    int row_num = 0;

        int i;
    for(i=0;i<music_track_num;i++){
        //初始化结构体和文件
        file_t *file = music_tracks[i].mid_file;
        memset(music_tracks+i,0,sizeof(music_tracks[i]));
        music_tracks[i].mid_file = file;
        file_seek(file,0,SEEK_SET);
        music_tracks[i].ch_num = i;
        music_tracks[i].read_pos = 0;
    }


    if(so&&so->trks>0){
        //轨道大于0
        int ch_num;
        ToneDataArr*arr_base = fix_rom(so->tone);
        cur_toneDataArr = arr_base;

        for(ch_num=0;ch_num<so->trks;ch_num++){
            u8 *mid = fix_rom(so->part[ch_num]);
            tracks[ch_num].mid = mid;
            tracks[ch_num].channel_num = ch_num;
            tracks[ch_num].clock = 0.0;
        }

        int is_end = 0;
        int debug_dump = 0;
        while(!is_end){
            //全部结束
            is_end = 1;
            for(ch_num=0;ch_num<so->trks;ch_num++){
                debug_dump++;
                if(debug_dump==67){
                    fprintf(stderr,"%d\n",debug_dump);
                }
                if(!tracks[ch_num].mid){
                    continue;
                }
                //如果还存在声道，则mid还存在
                is_end = 0;

                cur_tracks = &tracks[ch_num];
                u8  *mid = cur_tracks->mid;
                int mid_num = 0;

                cur_tracks->wait--;
                if(cur_tracks->wait<0)cur_tracks->wait = 0;

                if(cur_tracks->wait>0){
                    //该轨道正在等待
                    continue;
                }

                //结束等待

                //记录事件
                while(cur_tracks->mid&&cur_tracks->wait==0){  //如果没有遇到Wxx 事件
                    int event = mid[mid_num];
                    if(event<0x80){
                        //如果小于0x80，则表示是上一个事件的状态
                        event = cur_tracks->old_event;
                    }
                    else if(event>=0xbd){
                        //事件状态
                        cur_tracks->old_event = event;
                        mid_num ++;
                    }

                    mid_num += mid_event[event](mid + mid_num);

                    if(cur_tracks->note_down){
                        //如果被按下
                        musicplayer_track_t *track = find_noplay_track();
                        music_track_set(track,cur_tracks);
                        cur_tracks->note_down = 0;
                    }
                }
                //debug
                int off = cur_tracks->mid - (u8*)fix_rom(so->part[ch_num]);

                if(cur_tracks->mid){
                    //如果mid还存在，则
                    cur_tracks->mid += mid_num;
                }
            }

            //处理音乐播放
            for(ch_num=0;ch_num<music_track_num;ch_num++){
                // if(!music_tracks[ch_num].has){
                //     //表示后续声道都不存在了
                //     break;
                // }
                music_track_playing(music_tracks + ch_num);
            }
        }

        //生成xm文件
        int max_chnum = 0;
        file_seek(pat_file,0,SEEK_SET);
        row_num = -1;
        is_end = 0;

        while(!is_end){
            is_end = 1;
            row_num++;
            if(row_num==256){
                //写入一个pat中
                xm_gen_pat(pat_file,mid_file,255);
                pat_num++;
                row_num = 0;
                if(pat_num>=255){
                    break;
                }
            }
            if(row_num==13){
                fprintf(stderr,"%d\n",row_num);
            }
            for(ch_num=0;ch_num<music_track_num;ch_num++){
                if(!music_tracks[ch_num].has){
                    continue;
                }

                if(max_chnum<ch_num + 1){
                    max_chnum = ch_num + 1;
                }

                file_t *file = music_tracks[ch_num].mid_file;
                u8 next = 0x80;
                if(music_tracks[ch_num].read_pos>=file_tell(file)){
                    file_write(&next,1,1,mid_file);
                    continue;
                }
                is_end = 0;

                file_mem_t *fm = file_userdata(file);
                int read_pos = music_tracks[ch_num].read_pos;
                u8 *data = fm->data;

                int note = 0;
                int insn = 0;
                int vol = 0;    //力度
                int ext = 0;      //效果
                int eparam = 0; //效果参数


                while(1){
                    int e = data[read_pos++];
                    int v = data[read_pos++];
                    if(e==track_next)break;
                    if(e==track_note)note = v;
                    else if(e==track_insn)insn = v;
                    else if(e==track_vol) vol = v;
                    else if(e==track_note_off){
                        note = 0x61;
                    }
                    else if(e==track_portamento){
                        ext = 1;
                        eparam = v;
                    }
                    else if(e==track_portamento_down){
                        ext = 2;
                        eparam = v;
                    }
                }
                //设置read_pos
                music_tracks[ch_num].read_pos = read_pos;

                u8 bit = 0;
                u8 buf[0x10];
                int off = 0;
                if(note){
                    buf[off++] = note;
                    bit |= 1;
                }
                if(insn){
                    buf[off++] = insn;
                    bit |= 2;
                }
                //音量
                if(vol){
                    double v = (double)vol / 255.0;
                    v *= 64.0;
                    buf[off++] = v;
                    bit |= 4;
                }

                if(ext){
                    buf[off++] = ext;
                    buf[off++] = eparam;
                    bit |= 8|16;
                }

                if(bit==(1|2|4)){
                    buf[off++] = 0;
                    buf[off++] = 0;
                    bit |= 8 | 16;
                }

                if(bit==(1|2|4|8|16)){
                    file_write(buf,5,1,mid_file);
                }
                else if(!bit){
                    file_write(&next,1,1,mid_file);
                }
                else if(bit){
                    bit |= 0x80;
                    file_write(&bit,1,1,mid_file);
                    file_write(buf,off,1,mid_file);
                }
                else{
                    file_write(&next,1,1,mid_file);
                }
            }
        }

        if(row_num>0){
            //把当前记录的mid写入到pat文件中
            xm_gen_pat(pat_file,mid_file,row_num);
            pat_num++;
        }

        //file_seek(pat_file,0,SEEK_SET);
        FILE*xm_file = fopen("test.xm","wb+");
        double v = row_per_second(tempo); //每秒多少个row,如每秒25个row
        v = 1 / v;     //每个row需要多少秒
        xm_gen(xm_file,insn_file,insn_num,max_chnum,pat_file,pat_num,2.5/v);
        fclose(xm_file);

    }
}

static void* proc_music(){
    SongTable *song_table = 0;
    u32 addr = find_rom();
    insn_file = file_memopen(512);
    mid_file = file_memopen(512);
    pat_file = file_memopen(512);

    /*初始化*/
    int i;
    for(i=0;i<music_track_num;i++){
        memset(music_tracks+i,0,sizeof(music_tracks[i]));
        music_tracks[i].mid_file = file_memopen(512);
    }

    if(!addr){
        fprintf(stderr,"no find m4aSongNumStart:\n");
    }
    else{
        fprintf(stderr,"find m4aSongNumStart:%x\n",addr);
    }
    addr += 0x28;
    if(fix_rom(addr)){
        song_table = fix_rom(*(u32*)fix_rom(addr));
    }
    int idx = 0;
    if(dump_index){
        song_table += (dump_index - 1);
    }
    /*遇到0则结束*/
    while(song_table->so){
        song_proc(song_table);
        if(dump_index){
            break;
        }
        idx++;
        song_table++;
    }
}

int main(int argc,char**argv){
    _stderr = stdin;
    rom = rom_data(argv[1]);
    if(!rom){
        fprintf(stderr,"error rom:%s\n",argv[1]);
        return -1;
    }
    dump_index = 14+1;  //wario
    //dump_index = 13+1;  //agb2000
    proc_music();
    return 0;
}
